import {Component, Input, OnInit} from '@angular/core';
import {UtilComponent} from '../../interface/util-component.interface';
import Feature from 'ol/Feature.js';
import Map from 'ol/Map.js';
import View from 'ol/View.js';
import LineString from 'ol/geom/LineString.js';
import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer.js';
import Stamen from 'ol/source/Stamen.js';
import VectorSource from 'ol/source/Vector.js';
import {Stroke, Style} from 'ol/style.js';
import XYZ from 'ol/source/XYZ';

declare var arc: any;

@Component({
    selector: 'kylin-flight-animation',
    templateUrl: './flight-animation.component.html',
    styleUrls: ['./flight-animation.component.less']
})
export class FlightAnimationComponent implements OnInit, UtilComponent {
    // ---------- 接口实现 begin -------------- //
    hasContent = true;
    map: Map;

    @Input() set data(val) {
        Object.assign(this, val);
    };

    // ---------- 接口实现 end ---------------- //
    flightsLayer: VectorLayer;
    pointsPerMs = 0.1;
    style: Style;
    flightsSource: any;
    stamenLayer: any;
    timerList = new Array<any>();

    constructor() {
    }

    ngOnInit() {
        this.style = new Style({
            stroke: new Stroke({
                color: '#EAE911',
                width: 2
            })
        });

        this.flightsSource = new VectorSource({
            wrapX: false,
            attributions: 'Flight data by ' +
                '<a href="http://openflights.org/data.html">OpenFlights</a>,',
            loader: () => {
                const url = './assets/data/openflights/flights.json';
                fetch(url).then((response) => {
                    return response.json();
                }).then((json) => {
                    const flightsData = json.flights;
                    for (let i = 0; i < flightsData.length; i++) {
                        const flight = flightsData[i];
                        const from = flight[0];
                        const to = flight[1];

                        // create an arc circle between the two locations
                        const arcGenerator = new arc.GreatCircle(
                            {x: from[1], y: from[0]},
                            {x: to[1], y: to[0]});

                        const arcLine = arcGenerator.Arc(100, {offset: 10});
                        if (arcLine.geometries.length === 1) {
                            const line = new LineString(arcLine.geometries[0].coords);
                            line.transform('EPSG:4326', 'EPSG:3857');

                            const feature = new Feature({
                                crossOrigin: 'anonymous',
                                geometry: line,
                                finished: false
                            });
                            // add the feature with a delay so that the animation
                            // for all features does not start at the same time
                            this.addLater(feature, i * 50);
                        }
                    }
                    this.map.on('postcompose', this.animateFlights);
                });
            }
        });

        this.flightsLayer = new VectorLayer({
            source: this.flightsSource,
            style: (feature) => {
                // if the animation is still active for a feature, do not
                // render the feature with the layer style
                if (feature.get('finished')) {
                    return this.style;
                } else {
                    return null;
                }
            }
        });

        this.stamenLayer = new TileLayer({
            source: new XYZ({
                url: 'http://www.google.cn/maps/vt?lyrs=h@189&gl=cn&x={x}&y={y}&z={z}'
            })
        });
        this.map.addLayer(this.stamenLayer);
        this.map.addLayer(this.flightsLayer);

        this.map.render();
    }

    turnOn() {
    }

    turnOff() {
        this.map.removeLayer(this.flightsLayer);
        this.map.removeLayer(this.stamenLayer);
        this.timerList.map(item => {
            window.clearTimeout(item);
        })
    }

    cancel(options?: any): any {
        this.turnOff();
    }

    animateFlights = (event) => {
        const vectorContext = event.vectorContext;
        const frameState = event.frameState;
        vectorContext.setStyle(this.style);

        const features = this.flightsSource.getFeatures();
        for (let i = 0; i < features.length; i++) {
            const feature = features[i];
            if (!feature.get('finished')) {
                // only draw the lines for which the animation has not finished yet
                const coords = feature.getGeometry().getCoordinates();
                const elapsedTime = frameState.time - feature.get('start');
                const elapsedPoints = elapsedTime * this.pointsPerMs;

                if (elapsedPoints >= coords.length) {
                    feature.set('finished', true);
                }

                const maxIndex = Math.min(elapsedPoints, coords.length);
                const currentLine = new LineString(coords.slice(0, maxIndex));

                // directly draw the line with the vector context
                vectorContext.drawGeometry(currentLine);
            }
        }
        // tell OpenLayers to continue the animation
        this.map.render();
    }

    addLater = (feature, timeout) => {
        const timer = window.setTimeout(() => {
            feature.set('start', new Date().getTime());
            this.flightsSource.addFeature(feature);
        }, timeout);
        this.timerList.push(timer);
    }

}
